﻿#region Using directives

using System;
using System.IO;
using System.Reflection;
using System.Diagnostics;
using System.Collections.Generic;
using System.Text;

using Weazel.Gezel.Model.Errors;
using Weazel.Gezel.Parser;

#endregion

namespace Weazel.Gezel.Simulator.Reference
{
    class Program
    {
        public static void Main(string[] args)
        {
            // create the main simulation object
            Simulation simulation = new Simulation();

            // handle command line arguments
            long cycles = 1; // should be at least 1
            string filename = null;
            bool useStandardIn;
            parseArguments(simulation, args, ref cycles, ref filename, out useStandardIn);

            // get model description
            string description = null;
            if (useStandardIn)
            {
                // reading description from the console
                description = Console.In.ReadToEnd();
                filename = "stdin";
            }
            else
            {
                // reading description from a fdl file
                using (StreamReader reader = new StreamReader(filename))
                {
                    description = reader.ReadToEnd();
                }
            }

            // setup and run simulation			
            //try
            {
                // parse the file
                List<ParseError> parseErrors = new List<ParseError>();
                Weazel.Gezel.Model.Model model = ParseModel.Parse(ref parseErrors, description, filename);

                // print out any parse errors
                if (parseErrors.Count > 0)
                {
                    Console.WriteLine("Simulation was aborted because of these parse error(s):");
                    foreach (ParseError error in parseErrors)
                    {
                        Console.WriteLine();
                        Console.WriteLine(error.ToString());
                    }

                    // if the model contained errors
                    // don't try to setup simulation
                    Environment.Exit(1);
                }

                // verify the file
                List<VerificationError> verificationErrors = null;
                List<VerificationWarning> warnings = null;

                model.Verify(ref verificationErrors, ref warnings);

                // print errors if any
                if (verificationErrors.Count > 0)
                {
                    Console.Error.WriteLine("Simulation was aborted because of the these verification error(s):");
                    foreach (VerificationError error in verificationErrors)
                    {
                        Console.Error.WriteLine();
                        Console.Error.WriteLine(error.ToString());
                    }

                    // if the model contained errors
                    // don't try to setup simulation
                    Environment.Exit(1);
                }

                if (warnings.Count > 0)
                {
                    Console.Error.WriteLine("The following warning(s) was detected during verification:");
                    foreach (VerificationWarning warning in warnings)
                    {
                        Console.Error.WriteLine();
                        Console.Error.WriteLine(warning.ToString());
                    }
                }

                description = null; // allow GC on large models

                // start timing and simulate
                SimulationResult result = simulation.SetupAndRun(model, cycles);

                // Print toggle counts (if enabled)
                simulation.WriteToggleCounts();

                // print simulation results
                Console.Error.WriteLine(result.Message);
            }
            /*
  catch (ParseException ex)
  {
    Console.Error.WriteLine("Simulation aborted in cycle {0} with a parse error:\n\n{1}\n",
      simulation.Cycle,
      ex.Message
    );
  }
  catch (Exception ex)
  {
    Console.Error.WriteLine("Simulation aborted in cycle {0} with an unexpected error:\n\n{1}\n",
      simulation.Cycle,
      ex.Message
    );
        
    SimulationResult.TryLogException(ex);
  }		
             * */
        }

        /// <summary>
        /// Parse the command line arguments and initialize
        /// filename and number of cycles for simulation.
        /// 
        /// In case of invalid arguments, a brief usage description
        /// is shown before exit.
        /// </summary>
        /// <param name="args">the command line parameters</param>
        /// <param name="cycles">the number of cycles to run the simulation</param>
        /// <param name="filenameStr">the filename to read model from</param>
        /// <param name="useStandardIn">true iff the model should be read from standard and not from file</param>
        private static void parseArguments(Simulation simulation, string[] args, ref long cycles, ref string filenameStr, out bool useStandardIn)
        {
            string cyclesStr = null;
            string debugStr = null;
            useStandardIn = false;

            // handle command line args
            if (args.Length < 1 || args.Length > 3)
            {
                Console.Error.WriteLine("Wrong number of arguments");
                usage();
            }
            else if (args.Length == 1)
            {
                // assume that model input is to be read from standard in.
                // this is used for testing the reference simulator
                useStandardIn = true;
                cyclesStr = args[0];
            }
            else if (args.Length == 2)
            {
                debugStr = null;
                filenameStr = args[0];
                cyclesStr = args[1];
            }
            else if (args.Length == 3)
            {
                debugStr = args[0];
                filenameStr = args[1];
                cyclesStr = args[2];
            }

            // handle debug flag
            if (debugStr != null && debugStr == "-d")
            {
                simulation.DebugMode = true;
            }

            // check file input if not reading from 
            // the console (standard in)
            if (!useStandardIn && !File.Exists(filenameStr))
            {
                Console.Error.WriteLine("Cannot find file '{0}'", filenameStr);
                usage();
            }

            // try to get number of cycles to simulate
            try
            {
                cycles = long.Parse(cyclesStr);
                if (cycles < 1)
                {
                    Console.Error.WriteLine("Number of cycles must be positive: {0}", cycles);

                    // print usage and terminate
                    usage();
                }
            }
            catch
            {
                // check if the argument we have might be the name
                // of the model file to simulate in which case
                // the number of cycles is simply missing
                if (File.Exists(cyclesStr))
                {
                    Console.Error.WriteLine("Missing number of cycles argument");
                }
                else
                    Console.Error.WriteLine("Invalid number of cycles: '{0}'", cyclesStr);

                // print usage and terminate
                usage();
            }
        }

        /// <summary>
        /// Show a brief usage message and exit with exitcode 1 (error).
        /// </summary>
        private static void usage()
        {
            // get the name of the executable


            // disables because of security issues
            /*
            Assembly asm = Assembly.GetEntryAssembly();
            string executableName = asm.GetName().Name;
            */

            Console.Error.WriteLine("\nUsage:\n\n{0}  [-d] [files] <cycles>\n", "refsim"); //executableName);
            Console.Error.WriteLine("\nWhere:\n");
            Console.Error.WriteLine("-d\tenables extra debug mode output");
            Console.Error.WriteLine("file\tmodel files to run");
            Console.Error.WriteLine("cycles\tnumber of cycles to simulate");
            Console.Error.WriteLine("\nIf no files are specified the model is read from standard input");

            Environment.Exit(1);
        }
    }
}
